﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	/// <summary>
	/// This class is used to represent all bytecode operations that write a value from the eval stack into to simple locations (locals, arguments, etc.).
	/// </summary>
	public sealed class IRStoreFromEvalStackBasic : IRByteCode
	{
		/// <summary>
		/// An array containing all the acceptable opcodes for this operation class (in order)
		/// </summary>
		private static readonly OpCode.Op[] AcceptableOps = { OpCode.Op.StoreToLocal, OpCode.Op.StoreToGlobal };

		/// <summary>
		/// If this instruction is a store var operation.
		/// </summary>
		private readonly IRVarName m_vararg;

		/// <summary>
		/// If this instruction is a store static field operation.
		/// </summary>
		private readonly IRStaticField m_staticfield;

		/// <summary>
		/// True if this store updates the index position for a loop traversal.
		/// </summary>
		public bool IsTraverseLoopIndexAdvance;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant((this.OperationCode != OpCode.Op.StoreToLocal) | (this.m_vararg != null));
			Contract.Invariant((this.OperationCode != OpCode.Op.StoreToGlobal) | (this.m_staticfield != null));
		}

		private IRStoreFromEvalStackBasic(OpCode.Op opc, IRVarName vararg, IRStaticField sfield, IRSourceLocation sloc)
			: base(opc, sloc)
		{
			Contract.Requires(AcceptableOps.Contains(opc));
			
			this.m_vararg = vararg;
			this.m_staticfield = sfield;
		}

		public static IRStoreFromEvalStackBasic MakeVarStoreOperation(IRVarName vn, IRSourceLocation sloc)
		{
			Contract.Requires(vn != null);

			return new IRStoreFromEvalStackBasic(OpCode.Op.StoreToLocal, vn, null, sloc);
		}

		public static IRStoreFromEvalStackBasic MakeStaticFieldStoreOperation(IRStaticField sfld, IRSourceLocation sloc)
		{
			Contract.Requires(sfld != null);
			
			return new IRStoreFromEvalStackBasic(OpCode.Op.StoreToGlobal, null, sfld, sloc);
		}

		public override String ToString()
		{
			if(this.OperationCode == OpCode.Op.StoreToLocal)
				return "stvar:   " + this.m_vararg.ToString() + (this.IsTraverseLoopIndexAdvance ? " LoopAdvance" : "");
			else if(this.OperationCode == OpCode.Op.StoreToGlobal)
				return "stsfld:  " + this.m_staticfield.ToString();
			else
				return "--BAD OPCODE--";
		}

		/// <summary>
		/// If this is a variable argument store operation get the name of the targrt variable argument.
		/// </summary>
		public IRVarName LocalVar
		{
			get
			{
				Contract.Requires(this.OperationCode == OpCode.Op.StoreToLocal);
				Contract.Ensures(Contract.Result<IRVarName>() != null);

				return this.m_vararg;
			}
		}

		/// <summary>
		/// If this is a store to a static field this is the field location we are going to store to.
		/// </summary>
		public IRStaticField StaticField
		{
			get
			{
				Contract.Requires(this.OperationCode == OpCode.Op.StoreToGlobal);
				Contract.Ensures(Contract.Result<IRStaticField>() != null);

				return this.m_staticfield;
			}
		}
	}
}
